# -*- coding: utf-8 -*-

"""
 (c) 2023 - Copyright CTyunOS Inc

 Authors:
   youyifeng <youyf2@chinatelecom.cn>

"""
import datetime
import fcntl
import os.path

from .config import OPENEULER_PRODUCT_REPO_MAP, OSV_MAP
from .errors import InputDirNotDirError


def one_instance_check(lockfile):
    """
    Helper function for one instance checking
    """
    running_status = False
    lock_file_pointer = os.open(lockfile, os.O_CREAT | os.O_WRONLY)
    try:
        fcntl.lockf(lock_file_pointer, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError:
        running_status = True
    return running_status


def get_timestamp():
    """
    Helper function for getting timestamp
    """
    return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")


def notifier_timestamp():
    return datetime.datetime.now().strftime("%Y年%m月%d日%H时%M分%S秒")


def data_to_csv(list_of_dict, column_list: dict):
    """
    Helper function for dict to csv
    """
    import pandas as pd
    df = pd.DataFrame(list_of_dict)
    df.columns = column_list
    return df.to_csv(index=False)


def to_list(lst):
    """
    Helper function for py2/py3 compatibility
    """
    if isinstance(lst, list):
        return lst
    else:
        return list(lst)


def makesure_direxists(path):
    """
    Helper function for makesure_direxists
    """
    if os.path.isfile(path):
        print("file")
        dirname = os.path.basename(path)
    if os.path.isdir(path):
        print("path")
        if not os.path.exists(path):
            os.makedirs(dir, exist_ok=True)


def ensuredir(dir):
    """Make sure dir exists"""
    dir = os.path.normpath(dir)
    if os.path.exists(dir):
        if not os.path.isdir(dir):
            raise InputDirNotDirError("Not a dir: %s" % dir)
    else:
        try:
            os.makedirs(dir, exist_ok=True)
        except Exception as e:
            raise CreateWorkdirError("Create workdir %s error %s" % (dir, str(e)))


def parseRPM(rpm_filename):
    """parse RPM package"""
    if rpm_filename[-4:] == '.rpm':
        rpm_filename = rpm_filename[:-4]

    archIndex = rpm_filename.rfind('.')
    arch = rpm_filename[archIndex + 1:]

    relIndex = rpm_filename[:archIndex].rfind('-')
    rel = rpm_filename[relIndex + 1:archIndex]

    verIndex = rpm_filename[:relIndex].rfind('-')
    ver = rpm_filename[verIndex + 1:relIndex]

    epochIndex = rpm_filename.find(':')
    if epochIndex == -1:
        epoch = ''
    else:
        epoch = rpm_filename[:epochIndex]

    name = rpm_filename[epochIndex + 1:verIndex]
    return name, ver, rel, epoch, arch


def srpm_version_compare(old_srpm, new_srpm, osv=None):
    """compare srpm version"""
    import rpm
    _, over, orel, oepoch, _ = parseRPM(old_srpm)
    _, nver, nrel, nepoch, _ = parseRPM(new_srpm)

    if osv:
        # if define ovs, modify new_srpm rel segment value to ovs
        if osv not in OSV_MAP:
            nrel = '.'.join(nrel.split('.')[:-1]) + osv
        else:
            nrel = '.'.join(nrel.split('.')[:-1]) + OSV_MAP[osv]

    """
    version_utils.rpm.labelCompare(evr_a, evr_b)[source]
    Convenience function to provide the same behaviour as labelCompare from rpm-python.
    
    To be used as a drop-in replacement for labelCompare, thus the utilization of the non-standard camelCase variable name.
    
    To use the version_utils version and fall back to rpm:
    
    try:
        from version_utils.rpm import labelCompare
    except ImportError:
        from rpm import labelCompare

    Parameters:	
        evr_a (tuple) – an EVR tuple
        evr_b (tuple) – an EVR tuple

    """

    if rpm.labelCompare((oepoch, over, orel), (nepoch, nver, nrel)) < 0:
        return True
    return False


def find_higest_srpm_version(srpmnamelist):
    if 0 == len(srpmnamelist):
        Exception(" list can't be null")
    if 1 == len(srpmnamelist):
        return srpmnamelist[0]
    import rpm
    import functools

    def compare(old_srpm, new_srpm):
        """compare srpm version"""
        _, over, orel, oepoch, _ = parseRPM(old_srpm)
        _, nver, nrel, nepoch, _ = parseRPM(new_srpm)
        ret = rpm.labelCompare((oepoch, over, orel), (nepoch, nver, nrel))
        if ret < 0:
            return 1
        elif ret > 0:
            return -1
        else:
            return 0

    return sorted(srpmnamelist, key=functools.cmp_to_key(compare))[0]


def check_product(gconfig, options):
    productId = None
    # check repodata with cvrf, find diff
    if options.product:
        if options.product not in OPENEULER_PRODUCT_REPO_MAP:
            print("Unsupported product :", options.product)
            print("Support product:", "\n\t".join(p for p in OPENEULER_PRODUCT_REPO_MAP))
            exit(1)
        productId = options.product
    elif gconfig.PRODUCT:
        if gconfig.PRODUCT not in OPENEULER_PRODUCT_REPO_MAP:
            print("Unsupported product :", gconfig.PRODUCT)
            print("Support product:", "\n\t".join(p for p in OPENEULER_PRODUCT_REPO_MAP))
            exit(1)
        productId = gconfig.PRODUCT

    if not productId:
        print("no product found, you should use -p to specific it.")
        print("Support product:", "\n\t".join(p for p in OPENEULER_PRODUCT_REPO_MAP))
        exit(1)

    return productId


def do_download_file(url, path):
    import urllib
    with urllib.request.urlopen(url) as response, open(path, 'wb') as write_to:
        write_to.write(response.read())


def download_file(url, path, try_times=3):
    for try_index in range(try_times):
        try:
            do_download_file(url, path)
        except Exception as e:
            print("scrapy from '%s' error!" % url, str(e))
            if try_index == try_times - 1:
                print("try [%d] times failed! exit.")
                exit(1)
            print(" try again [%d/%d] " % (try_index + 1, try_times))
            continue
        break


def ungzfile(gzfile, output):
    import gzip
    with gzip.open(gzfile, 'rb') as fin, open(output, 'wb') as fout:
        buf = fin.read()
        if len(buf) > 1:
            fout.write(buf)


def get_wanip():
    import requests
    url = "http://ifconfig.me/ip"
    try:
        response = requests.get(url, timeout=10)
        if response.status_code == 200:
            return response.text.strip()
        return "unkown"
    except Exception:
        return "error"


def get_watcher(gconfig):
    """
    {
        "all_pkg_watcher" : {
            "watch_pkg" : [ "ALL" ],
            "watch_score" : 0.0,
            "dingding_notifier" : [ "key_update_notifier", "key_status_notifier"],
            "wecom_notifier" : [ "key_update_notifier", "key_status_notifier"],
        },
        "kernel_pkg_watcher" : {
            "watch_pkg" : [ "kernel" ],
            "watch_score" : 0.0,
            "dingding_notifier" : [ "key_update_notifier", "key_status_notifier"],
            "wecom_notifier" : [ "key_update_notifier", "key_status_notifier"],
        },
        "hotpkg_watcher" : {
            "watch_pkg" : [ "nginx","openssh","openssl","sudo" ],
            "watch_score" : 0.0,
            "dingding_notifier" : [ "key_update_notifier", "key_status_notifier"],
            "wecom_notifier" : [ "key_update_notifier", "key_status_notifier"],
        },
        "higher_score_watcher" : {
            "watch_pkg" : [  ],
            "watch_score" : 8.0,
            "dingding_notifier" : [ "key_update_notifier", "key_status_notifier"],
            "wecom_notifier" : [ "key_update_notifier", "key_status_notifier"],
        }
    }
    """
    watcher = {}
    for item in gconfig.config:
        section = gconfig.config[item]
        if 'watch_pkg' not in section and 'watch_score' not in section:
            continue

        watcher[item] = {}
        watcher[item]['watch_score'] = 0.0
        watcher[item]['watch_pkg'] = []
        watcher[item]['info_type'] = []
        # check section['watch_pkg']
        if 'watch_pkg' in section:
            for pkg in section['watch_pkg'].split(','):
                if "" != pkg.strip():
                    watcher[item]['watch_pkg'].append(pkg.strip())
        # check section['watch_score']
        try:
            watcher[item]['watch_score'] = float(section['watch_score'])
        except Exception:
            # ignore error, just set watch_score = 0.0
            pass
        # check section['info_type']
        for pkg in section['info_type'].split(','):
            if "" != pkg.strip():
                watcher[item]['info_type'].append(pkg.strip())
        if 0 == len(watcher[item]['info_type']):
            continue

        if ('update_notify_enabled' in section and section['update_notify_enabled'] == '1') or \
                ('status_notify_enabled' in section and section['status_notify_enabled'] == '1'):
            for key in section:
                if "" == section[key].strip():
                    continue
                if "watch_pkg" == key or "info_type" == key or "watch_score" == key:
                    continue
                if key.endswith('_notifier'):
                    # if is notifier
                    watcher[item][key] = []
                    for k in section[key].strip().split(','):
                        watcher[item][key].append(k.strip())
                else:
                    # other key just add
                    watcher[item][key] = section[key]

    return watcher
